<template>
  <el-card v-if="isInit && table.IsHidden == false" class="table">
    <div slot="header">
      <span>{{ table.Title }}</span>
      <el-button
        v-if="tableSet.RowGenerateMode == 0 && isLock == false && readonly == false && isShowBtn"
        type="success"
        size="mini"
        style="float: right"
        @click="add"
      >添加一行</el-button>
    </div>
    <el-form :ref="table.Id" :model="rowData">
      <w-table
        :data="rows"
        :columns="columns"
        :is-paging="tableSet.IsPaging"
        :paging="paging"
        row-key="rowId"
        :is-show-no="tableSet.IsShowNo"
        @load="loadRow"
      >
        <template slot="control" slot-scope="e">
          <el-form-item
            :key="e.column.key+'_'+ver"
            :prop="'rows[' + e.row.rowId + '].' + e.column.name"
            :rules="e.column.rules"
            :class="isHidden(e.column,e.row)"
          >
            <span
              slot="error"
              slot-scope="ex"
              :class="ex.error.startsWith('0,') ? 'warn' : 'error'"
            >{{
              ex.error.startsWith("0,") || ex.error.startsWith("1,")
                ? ex.error.substring(2)
                : ex.error
            }}</span>
            <userControl
              :key="e.column.key + '_' + e.row.rowId"
              v-model="e.row[e.column.name]"
              :link-biz-pk="linkBizPk"
              :is-def-value.sync="e.row.defState[e.column.key]"
              :row-id="e.row.rowId"
              :table="table"
              :rows="rows"
              :state="getState(e.column, e.row)"
              :col-id="e.column.key"
              :control="colCache[e.column.key]"
              :readonly="isReadonly(e.column.key)"
              :is-watch="isWatch(e.column.key)"
              :source="e.row"
              @change="controlChange"
            />
          </el-form-item>
        </template>
        <template slot="action" slot-scope="e">
          <el-button
            v-if="
              tableSet.RowGenerateMode == 0 ||
                (tableSet.RowGenerateMode == 2 && maxRowNum != rowNum)
            "
            icon="el-icon-delete"
            size="mini"
            circle
            type="danger"
            @click="drop(e.row)"
          />
        </template>
      </w-table>
    </el-form>
  </el-card>
  <div v-else-if="isInit && table.IsHidden" class="table">
    <w-table
      :data="rows"
      :columns="columns"
      :is-paging="tableSet.IsPaging"
      :paging="paging"
      row-key="rowId"
      :is-show-no="tableSet.IsShowNo"
      @load="loadRow"
    >
      <template slot="control" slot-scope="e">
        <el-form-item
          :key="e.column.key+'_'+ver"
          :prop="'rows[' + e.row.rowId + '].' + e.column.name"
          :rules="e.column.rules"
          :class="isHidden(e.column,e.row)"
        >
          <span
            slot="error"
            slot-scope="ex"
            :class="ex.error.startsWith('0,') ? 'warn' : 'error'"
          >{{
            ex.error.startsWith("0,") || ex.error.startsWith("1,")
              ? ex.error.substring(2)
              : ex.error
          }}</span>
          <userControl
            :key="e.column.key + '_'+e.row.rowId"
            v-model="e.row[e.column.name]"
            :link-biz-pk="linkBizPk"
            :row-id="e.row.rowId"
            :rows="rows"
            :state="getState(e.column, e.row)"
            :table="table"
            :col-id="e.column.key"
            :is-def-value.sync="e.row.defState[e.column.key]"
            :control="colCache[e.column.key]"
            :readonly="isReadonly(e.column.key)"
            :is-watch="isWatch(e.column.key)"
            :source="e.row"
            @change="controlChange"
          />
        </el-form-item>
      </template>
      <template slot="action" slot-scope="e">
        <el-button
          v-if="
            tableSet.RowGenerateMode == 0 ||
              (tableSet.RowGenerateMode == 2 && maxRowNum != rowNum)
          "
          icon="el-icon-delete"
          size="mini"
          circle
          type="danger"
          @click="drop(e.row)"
        />
      </template>
    </w-table>
  </div>
</template>

<script>
import userControl from './userControl.vue'
export default {
  components: {
    userControl
  },
  props: {
    table: {
      type: Object,
      default: null
    },
    dataSource: {
      type: Object,
      default: null
    },
    linkBizPk: {
      type: String,
      default: null
    },
    power: {
      type: Object,
      default: null
    },
    watchCol: {
      type: Object,
      default: null
    },
    isShowBtn: {
      type: Boolean,
      default: true
    },
    readonly: {
      type: Boolean,
      default: false
    },
    defState: {
      type: Object,
      default: null
    }
  },
  data() {
    return {
      rows: [],
      dataRows: [],
      columns: [],
      colCache: {},
      watchColId: [],
      rowId: 1,
      rowNum: 0,
      rowIndex: 0,
      maxRowNum: 0,
      isInit: false,
      rowData: {
        rows: {}
      },
      ver: 1,
      rules: {
        xm: [
          {
            required: true,
            message: '姓名不能为空！',
            trigger: 'blur'
          }
        ]
      },
      isLock: false,
      mergeCol: [],
      tableSet: {
        RowGenerateMode: 0,
        TableId: null,
        GenerateCol: null,
        FixedRowNum: null,
        FillRowNum: null,
        FillColumn: null,
        IsRowCheck: null,
        IsPaging: false,
        IsShowNo: false
      },
      paging: {
        Size: 20,
        Index: 1,
        SortName: 'RowIndex',
        IsDesc: false,
        Total: 0
      },
      rowTemplate: null
    }
  },
  watch: {
    table: {
      handler(val) {
        if (val) {
          this.reset()
        }
      },
      immediate: true
    }
  },
  mounted() {},
  methods: {
    isWatch(key) {
      return this.watchColId.includes(key)
    },
    isHidden(col, row) {
      if (row.ColState == null || row.ColState[col.key] == null) {
        return col.state === 1 ? 'hidden' : null
      }
      return row.ColState[col.key] === 1 ? 'hidden' : null
    },
    getState(col, row) {
      if (row.ColState == null || row.ColState[col.key] == null) {
        return col.state
      }
      return row.ColState[col.key]
    },
    controlChange(e) {
      this.$emit('change', e)
    },
    isReadonly(key) {
      if (this.readonly) {
        return true
      } else if (this.power == null) {
        return false
      }
      const opt = this.power[key]
      return opt != null && opt === 0
    },
    checkIsValidator(row) {
      for (const i in this.colCache) {
        if (row.defState[i] === false) {
          return true
        }
      }
      return false
    },
    checkColIsEdit(row, id) {
      let state = null
      if (row.ColState != null) {
        state = row.ColState[id]
      }
      if (state == null && this.defState != null) {
        state = this.defState[id]
      }
      if (state != null && state !== 0) {
        return false
      }
      return true
    },
    validateRow(rule, value) {
      if (rule.RuleType === 1) {
        return rule.pattern.test(value)
      } else if (rule.RuleType === 0) {
        if (typeof value !== 'number') {
          value = Number.parseFloat(value)
        }
        if (rule.Min == null && rule.Max == null) {
          return true
        } else {
          if (rule.Min != null) {
            return rule.MinIsEqual ? value >= rule.Min : value > rule.Min
          }
          if (rule.Max != null) {
            return rule.MaxIsEqual ? value <= rule.Max : value < rule.Max
          }
        }
      } else if (rule.RuleType === 2 && rule.MinLen != null) {
        if (typeof value !== 'string') {
          value = value.toString()
        }
        return rule.MinLen <= value.length
      } else {
        return true
      }
    },
    dataValidator(rule, value, callback) {
      const end = rule.field.lastIndexOf(']')
      const rowId = parseInt(rule.field.substring(5, end))
      const row = this.rowData.rows[rowId]
      if (!this.checkColIsEdit(row, rule.key)) {
        callback()
        return
      }
      let error = null
      if ((value == null || value === '')) {
        if (rule.col.IsNotNull) {
          error = '1,' + rule.label + '不能为空!'
        }
      } else {
        const col = rule.col
        if (col.ValidateRule != null && col.ValidateRule.length !== 0) {
          let msg = null
          col.ValidateRule.forEach((a) => {
            if (!this.validateRow(a, value)) {
              if (msg == null) {
                msg = a
              } else if (msg.IsWarn === true && a.IsWarn === false) {
                msg = a
              }
            }
          })
          if (msg != null) {
            error = (msg.IsWarn ? '0,' : '1,') + msg.Message
          }
        }
      }
      if (error == null) {
        callback()
      } else if (this.tableSet.IsRowCheck) {
        if (this.checkIsValidator(row)) {
          callback(error)
        } else {
          callback()
        }
      } else {
        callback(error)
      }
    },
    getRules(column) {
      const col = column.Column
      if (col.IsNotNull === false && (col.ValidateRule == null || col.ValidateRule.length === 0)) {
        return []
      }
      if (col.ValidateRule != null && col.ValidateRule.length !== 0) {
        col.ValidateRule.forEach((a) => {
          if (a.RuleType === 1) {
            a.pattern = new RegExp(a.Regex)
          }
        })
      }
      return {
        key: column.Key,
        trigger: 'blur',
        label: column.Label,
        col: col,
        validator: this.dataValidator
      }
    },
    loadRow(paging) {
      const start = (paging.Index - 1) * paging.Size
      let end = start + paging.Size
      if (end > this.dataRows.length) {
        end = this.dataRows.length
      }
      this.rows = this.dataRows.slice(start, end)
    },
    drop(row) {
      if (this.tableSet.IsPaging) {
        const index = this.dataRows.findIndex((a) => a.rowId === row.rowId)
        this.dataRows.splice(index, 1)
        this.rowNum = this.dataRows.length
        if (!this.resetPaging()) {
          this.loadRow(this.paging)
        }
      } else {
        const index = this.rows.findIndex((a) => a.rowId === row.rowId)
        this.rows.splice(index, 1)
        this.rowNum = this.rows.length
      }
      this.rowChange('drop', row)
    },
    rowChange(op, row) {
      if (this.watchColId.length === 0) {
        return
      }
      const data = {
        op,
        row,
        colId: this.watchColId,
        tableId: this.table.Id
      }
      this.$emit('rowChange', data)
    },
    reset() {
      const cols = []
      const row = {}
      this.colCache = {}
      this.mergeCol = []
      let state = this.defState
      if (state == null) {
        state = {}
      }
      if (this.table.Columns && this.table.Columns.length > 0) {
        this.table.Columns.forEach((a) => {
          if (this.power != null && this.power[a.Key] === 2) {
            return
          }
          const col = {
            key: a.Key,
            title: a.Label,
            align: 'center'
          }
          col.state = state[a.Key]
          if (col.state == null) {
            col.state = 0
          }
          if (a.Column != null) {
            const column = a.Column
            if (column.Align === 0) {
              col.align = 'left'
            } else if (column.Align === 2) {
              col.align = 'right'
            }
            col.name = column.Name
            col.minWidth = column.Width == null ? '100px' : column.Width + 'px'
            col.slotName = 'control'
            col.rules = this.getRules(a)
            this.colCache[col.key] = column
            row[column.Name] = null
            cols.push(col)
            if (column.IsMergeRow) {
              this.mergeCol.push({
                key: col.key,
                name: col.name
              })
            }
            if (this.watchCol && this.watchCol[col.key] != null) {
              this.watchColId.push(col.key)
            }
          } else if (a.Children != null && a.Children.length > 0) {
            col.children = []
            this.initColumns(a, col, row, state)
            cols.push(col)
          }
        })
      }
      this.isLock = this.mergeCol.length > 0
      this.rowTemplate = row
      if (this.table.TableSet != null) {
        this.tableSet.RowGenerateMode = this.table.TableSet.RowGenerateMode
        this.tableSet.GenerateCol = this.table.TableSet.GenerateCol
        this.tableSet.TableId = this.table.TableSet.TableId
        this.tableSet.FixedRowNum = this.table.TableSet.FixedRowNum
        this.tableSet.FillRowNum = this.table.TableSet.FillRowNum
        this.tableSet.IsRowCheck = this.table.TableSet.IsRowCheck
        this.tableSet.IsPaging = this.table.TableSet.IsPaging
        this.tableSet.IsShowNo = this.table.TableSet.IsShowNo
        this.paging.Size = this.table.TableSet.PageSize
        this.paging.Index = 1
      }
      if (this.tableSet.RowGenerateMode === 2) {
        if (this.tableSet.TableId != null && this.tableSet.GenerateCol != null) {
          // eslint-disable-next-line no-prototype-builtins
          if (!this.dataSource[this.tableSet.TableId].hasOwnProperty(this.tableSet.GenerateCol)) {
            this.dataSource[this.tableSet.TableId] = 0
          }
          this.$watch(
            () => {
              return this.dataSource[this.tableSet.TableId][this.tableSet.GenerateCol]
            },
            (newVal, oldVal) => {
              if (newVal !== oldVal) {
                this.initRow()
              }
            },
            { flush: 'post' }
          )
        }
      }
      if (this.isLock === false && (this.tableSet.RowGenerateMode === 0 || this.tableSet.RowGenerateMode === 2) && this.isShowBtn && this.readonly === false) {
        cols.push({
          title: '操作',
          align: 'left',
          slotName: 'action'
        })
      }
      this.columns = cols
      this.isInit = true
      this.$nextTick(this.loadRows)
    },
    loadRows() {
      const rows = this.dataSource[this.table.Id]
      if (rows.length > 0) {
        this.formatRow(rows)
      }
      if (this.tableSet.IsPaging) {
        this.dataRows = rows
      } else {
        this.rows = rows
      }
      if (rows.length === 0) {
        this.initRow()
      }
    },
    refresh() {
      this.ver = this.ver + 1
    },
    formatRow(rows) {
      this.rowId = rows[rows.length - 1].rowId + 1
      this.rowNum = rows.length
      if (this.mergeCol.length > 0) {
        this.mergeCol.forEach(c => {
          this.mergeRow(rows, c)
        })
      }
      rows.forEach(c => {
        c.defState = {}
        this.rowData.rows[c.rowId] = c
      })
    },
    mergeRow(rows, col) {
      let mergeKey = null
      let index = 0
      let curIndex = 0
      let merge = 1
      rows.forEach((c) => {
        const key = c[col.name]
        if (mergeKey == null) {
          mergeKey = key
          index = curIndex
        } else if (mergeKey !== key) {
          mergeKey = key
          const num = curIndex - index
          if (num > 1) {
            const r = rows[index]
            if (r.rowSpan == null) {
              r.rowSpan = {}
            }
            r.rowSpan[col.key] = num
          }
          merge = merge + 1
          index = curIndex
        } else {
          if (c.rowSpan == null) {
            c.rowSpan = {}
          }
          c.rowSpan[col.key] = 0
        }
        curIndex = curIndex + 1
      })
      const num = curIndex - index
      if (num > 1) {
        const r = rows[index]
        if (r.rowSpan == null) {
          r.rowSpan = {}
        }
        r.rowSpan[col.key] = num
      }
    },
    validate() {
      return new Promise((resolve, reject) => {
        this.$refs[this.table.Id].validate((valid, error) => {
          if (valid) {
            resolve(true)
          } else if (this.checkIsWarn(error)) {
            resolve(true)
          } else {
            resolve(false)
          }
        })
      })
    },
    checkIsWarn(error) {
      for (const i in error) {
        const err = error[i]
        const index = err.findIndex(c => !c.message.startsWith('0,'))
        if (index !== -1) {
          return false
        }
      }
      return true
    },
    initRow() {
      if (
        this.tableSet.RowGenerateMode === 1 &&
        this.tableSet.FixedRowNum != null
      ) {
        for (let i = 0; i < this.tableSet.FillRowNum; i++) {
          this.addRow()
        }
        this.rowChange('adds', this.rowTemplate)
      } else if (
        this.tableSet.RowGenerateMode === 2
      ) {
        this.maxRowNum = this.dataSource[this.tableSet.TableId][this.tableSet.GenerateCol]
        if (this.maxRowNum > this.rowNum) {
          const add = this.maxRowNum - this.rowNum
          for (let i = 0; i < add; i++) {
            this.addRow()
          }
          this.rowChange('adds', this.rowTemplate)
        }
      }
    },
    cloneRow() {
      const row = {}
      for (const i in this.rowTemplate) {
        row[i] = null
      }
      row.rowId = this.rowId
      row.defState = {}
      this.rowId = this.rowId + 1
      this.rowData.rows[row.rowId] = row
      return row
    },
    add() {
      this.addRow()
      this.rowChange('add', this.rowTemplate)
    },
    addRow() {
      const row = this.cloneRow()
      if (this.tableSet.IsPaging) {
        this.dataRows.push(row)
        if (!this.resetPaging()) {
          this.rows.push(row)
        }
      } else {
        this.rows.push(row)
        this.rowNum = this.rows.length
      }
    },
    resetPaging() {
      const len = this.dataRows.length
      this.paging.Total = len
      this.rowNum = len
      const index = Math.ceil(len / this.paging.Size)
      if (index !== this.paging.Index) {
        this.paging.Index = index
        return true
      }
      return false
    },
    initColumns(column, col, row, state) {
      column.Children.forEach((a) => {
        const t = {
          key: a.Key,
          title: a.Label,
          align: 'left'
        }
        t.state = state[a.Key]
        if (t.state == null) {
          t.state = 0
        }
        if (a.Column != null) {
          const column = a.Column
          t.name = column.Name
          t.minWidth = column.Width == null ? '100px' : column.Width + 'px'
          t.slotName = 'control'
          t.rules = this.getRules(a)
          row[column.Name] = null
          this.colCache[t.key] = column
          col.children.push(t)
          if (column.IsMergeRow) {
            this.mergeCol.push({
              key: t.key,
              name: t.name
            })
          }
        } else if (a.Children != null && a.Children.length > 0) {
          t.children = []
          this.initColumns(a, t)
          col.children.push(t)
        }
      })
    }
  }
}
</script>

<style scoped>
.table {
  margin-bottom: 10px;
}
.table .el-form-item {
  margin-bottom: 0px !important;
}
.table .el-radio-group {
  width: 100%;
}
.table .el-date-editor.el-input {
  width: 100% !important;
}
.table .el-input-number--medium {
  width: 100% !important;
}
.table .error {
  color: #ff4949;
}
.table .warn {
  color: #ffae00;
}

.hidden {
  display: none;
}
.table .el-radio-group:has(.is-disabled) {
  .is-active {
    .el-radio-button__inner {
      background-color: #1890ff !important;
      color:#fff;
    }
  }
}

.table .is-error:has(.warn) {
  .el-input__inner {
    border-color: #ffae00 !important;
  }
}
</style>
